<?php
namespace app\shop\library;

class Jwt{

    public $errorCode = '';
    public $errorMsg = '';

    private  $config  =  array();

    private  function header(){
        return [
            'alg' => $this->config['alg'] ?? 'HS256',
            'typ' => 'JWT'
        ];
    }

    public function __construct($config)
    {   
        $this->config = $config;
    }

    private  function payload(array $data){
        

        $_data['iss'] =  $this->config['iss']?? 'bi-shop';
        $_data['iat'] = time();
        $_data['exp'] = time() + ( $this->config['exp'] ?? 3600*24*10);
        $_data['data'] = $data;
         
        return $_data;

    }

    public  function encode(array $data){
        $data = $this->payload($data);
        $header = $this->header();
        if (is_array($data)){
            $base64header = $this->base64UrlEncode(json_encode($header ,JSON_UNESCAPED_UNICODE));
            $base64payload = $this->base64UrlEncode(json_encode($data,JSON_UNESCAPED_UNICODE));

            $token = $base64header.'.'.$base64payload.'.'.$this->signature($base64header.'.'.$base64payload,$header['alg']);

            return $token;
        }else{
            return false;
        }

    }

    public  function decode(string $token){
        if(strpos($token,'.') !== false){
            $tokenArr = explode('.',$token);
            $base64header = $tokenArr[0];
            $base64payload = $tokenArr[1];
            $signature = $tokenArr[2];
            $header = json_decode($this->base64UrlDecode($base64header),true);

            if($this->signature($base64header.'.'.$base64payload, $header['alg']) === $signature){
                
                $payload = json_decode($this->base64UrlDecode($base64payload),true);
            }else{

                $this->errorCode = 'signature';
                $this->errorMsg = '签名验证失败';
                return false;
            }
            
            // 验证是否过期
            if(isset($payload['exp']) && $payload['exp'] < time()){
                $this->errorCode = 'exp';
                $this->errorMsg = 'TOKEN 已过期';
                return false;
            }

            if(isset($payload['nbf']) && $payload['nbf'] > time()){
                return false;
            }

            return $payload['data']??false;
        }else{
            return false;
        }
    }

    /**
     * base64UrlEncode  https://jwt.io/ 中base64UrlEncode编码实现
     * @param string $input 需要编码的字符串
     * @return string
     */
    private function base64UrlEncode(string $input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
     * @param string $input 需要解码的字符串
     * @return bool|string
     */
    private  function base64UrlDecode(string $input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input .= str_repeat('=', $addlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * HMACSHA256签名  https://jwt.io/ 中HMACSHA256签名实现
     * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
     * @param string $key
     * @param string $alg  算法方式
     * @return mixed
     */
    private  function signature(string $input,string $alg)
    {

        $key = $this->config['key']??'bi-shop';
        
        $alg_config = array(
            'HS256' => 'sha256',
            'HS384' => 'sha384',
            'HS512' => 'sha512'
        );

        $hash = '';

        if (isset($alg_config[$alg])){
            $hash = hash_hmac($alg_config[$alg], $input, $key, true);
        }else{
            if ($alg == 'md5') {
                $hash = md5($input.$key, true);
            }
        }

        return $this->base64UrlEncode($hash);
    }

}